#include <dinput.h>

#define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

#define SAMPLE_BUFFER_SIZE 16

LPDIRECTINPUT8       g_pDI       = NULL;        
LPDIRECTINPUTDEVICE8 g_pKeyboard = NULL;
LPDIRECTINPUTDEVICE8 g_pMouse	 = NULL;  

LPDIRECTINPUTDEVICE8 g_pJoystick[4] = {0}; 
LPDIRECTINPUTDEVICE8 g_pJoystickNum = NULL; 
DWORD                 numforces=4;
LPDIRECTINPUTEFFECT  g_pEffect[4*8] = {0};
DWORD                 g_dwNumForceFeedbackAxis[4] = {0};


    DIEFFECT eff;
	DIENVELOPE en;
	DIPERIODIC cf;
	DICONSTANTFORCE cfw;
	DICONDITION c;
	
DWORD jpoint = 0;
DWORD joyid[4] = {0};
DWORD jyindex[4] = {0};
int iswheel[4] = {0};
    BYTE diks[256] = {0};		  // DirectInput keyboard state buffer 
	DIMOUSESTATE2 dims2 = {0};      // DirectInput mouse state structure
	DIJOYSTATE2 js[4] = {0};           // DInput joystick state 
	int jindex = NULL;
	int iindex = NULL;
	int numj = NULL;
	char jname[4][255] = {0};
    //DIPROPDWORD dipdw;
	int useforce[4] = {0};
	int forcepercent[4] = {0};
	int axislinear[4][4] = {0};
	int wheelspring[4] = {0};
	int wheelinertia[4] = {0};
	int forcegain[4] = {0};
	int forcefgain[4][4] = {0};
	int forcefade[4][4] = {0};
	int forcefadein[4][4] = {0};
	int forcefadeout[4][4] = {0};
	int forcestart[4][4] = {0};
	int forcefreq[2][4] = {0};
	int forceoffset[2][4] = {0};
	int usesecond=0;
	int kd=0;
	int autoswitch;
	int autoid;
	int didfree=0;
	int initbeep=0;
	int offset;
	DWORD magicnum = 123456;
	long magold[4][4];
	long magcur[4][4];
	long magnew[4][4][1000];
	long magrol[4][4];
	long magtmp[4][4];
	long magtim[4][4];
	long magcur2[4][4];
	long magspd[4][4];
	HWND hookWnd;

	

WNDPROC wpOriginalWndProc = NULL; 
void FreeDirectInput(int dll);
LRESULT APIENTRY NewWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 	

		switch (uMsg)
		{	
			case WM_DESTROY:
			{	
				//Beep(100,100);
				FreeDirectInput(0);
				//PostQuitMessage(0);
				break;
			}

			case WM_ACTIVATE:
			{
				if( WA_INACTIVE != wParam )
				{
					for (int p = 0; p < 4; ++p)
					{

						if (g_pJoystick[p])
						{
							// Make sure the device is acquired, if we are gaining focus.
							g_pJoystick[p]->Acquire();
							if (useforce[p]==1)
							{
								for (int f = 0; f < numforces; ++f)
								{
								if( g_pEffect[p*numforces+f] ) 
									g_pEffect[p*numforces+f]->Start( 1, 0 ); // Start the effect
								}
							}

						}
					}
				}
				break;
			}
		}
			
    return CallWindowProc(wpOriginalWndProc, hWnd, uMsg, wParam, lParam); 
} 
void FreeDirectInput(int dll)
{
	if (didfree) return;
	didfree=1;
	if(wpOriginalWndProc) SetWindowLongW(hookWnd, GWL_WNDPROC, (LONG) wpOriginalWndProc); 
    // Unacquire the device one last time just in case 
    // the app tried to exit while the device is still acquired.

    if( g_pKeyboard ) 
	{
        g_pKeyboard->Unacquire();
		g_pKeyboard->Release();
		g_pKeyboard = NULL;
	}

	if( g_pMouse ) 
	{
        g_pMouse->Unacquire();
		g_pMouse->Release();
		g_pMouse = NULL;
	}
    // Release any DirectInput objects.

	for (int p = 0; p < 4; ++p)
	{
		if( g_pJoystick[p] )
		{
			for (int f = 0; f < numforces; ++f)
			{
				if( g_pEffect[p*numforces+f] ) 
				{
					g_pEffect[p*numforces+f]->Stop();
					SAFE_RELEASE( g_pEffect[p*numforces+f]);
					g_pEffect[p*numforces+f] = NULL;
					
				}
			}
			g_dwNumForceFeedbackAxis[p]=0;
			g_pJoystick[p]->Unacquire();
			if(!dll)g_pJoystick[p]->Release();
			g_pJoystick[p] = NULL;
		}
	}
	if( g_pDI ) 
	{
		g_pDI->Release();
		g_pDI=NULL;
	}

	//iindex = NULL;
}

BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
                                     VOID* pContext )
{

	if (iindex == jindex)
	{
		
		g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick[jpoint], NULL );
		if(g_pJoystick[jpoint])
		{
			iindex = 0;
			strcpy(jname[jpoint],pdidInstance->tszProductName);
			
		}
		return DIENUM_STOP;
	}
	else
	{
		++iindex;
		return DIENUM_CONTINUE;
	}

}

BOOL CALLBACK EnumJoysticksNumCallback( const DIDEVICEINSTANCE* pdidInstance,
                                     VOID* pContext )
{
	//HRESULT hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystickNum, NULL );
	//if (!FAILED(hr)) ++numj;
	++numj;
	return DIENUM_CONTINUE;

}

BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
                                   VOID* pContext )
{

    // For axes that are returned, set the DIPROP_RANGE property for the
    // enumerated axis in order to scale min/max values.
    if( pdidoi->dwType & DIDFT_AXIS )
    {
        DIPROPRANGE diprg; 
        diprg.diph.dwSize       = sizeof(DIPROPRANGE); 
        diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); 
        diprg.diph.dwHow        = DIPH_BYID; 
        diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
        diprg.lMin              = -32767; 
        diprg.lMax              = +32767; 
    
        // Set the range for the axis
        if( FAILED( g_pJoystick[jpoint]->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) 
            return DIENUM_STOP;
      
    }

    return DIENUM_CONTINUE;
}

BOOL CALLBACK EnumAxesCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
                                VOID* pContext )
{
    DWORD* pdwNumForceFeedbackAxis = (DWORD*) pContext;

    if( (pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
        (*pdwNumForceFeedbackAxis)++;

    return DIENUM_CONTINUE;
}

void PollDevices()
{
    HRESULT hr;   

	//KEYBOARD
    if( g_pKeyboard ) 
	{
		ZeroMemory( diks, sizeof(diks) );
		hr = g_pKeyboard->GetDeviceState( sizeof(diks), diks );
		if( FAILED(hr) ) 
		{
			g_pKeyboard->Acquire();
		}
	}

	//MOUSE
    if( g_pMouse ) 
	{
		ZeroMemory( &dims2, sizeof(dims2) );
		hr = g_pMouse->GetDeviceState( sizeof(DIMOUSESTATE2), &dims2 );

		if( FAILED(hr) ) 
			g_pMouse->Acquire();
	}

	//JOYSTICK
	for (int p = 0; p < 4; ++p)
	{
		//if(jyindex[p] > -1)
		//{
			if( g_pJoystick[p] ) 
			{
				hr = g_pJoystick[p]->Poll(); 
				if( FAILED(hr) )  
				{
					g_pJoystick[p]->Acquire();

				}
				else
				{
					g_pJoystick[p]->GetDeviceState( sizeof(DIJOYSTATE2), &js[p] );
				}
			}
		//}
	}
}
bool KeyDown(DWORD KeyID, DWORD idx)
{
	//if(jyindex[idx] == -1) return false;
	//Check keyboard
	if (KeyID < 256)
	{
		if( g_pKeyboard ) 
			if ( diks[KeyID] & 0x80 )
				return true;
	}
	//Check mouse
	if ((KeyID > 256) & (KeyID < 265)) //branco
	{
		if( g_pMouse ) 
			if ( dims2.rgbButtons[KeyID - 257] & 0x80 )
				return true;
	}
	//Check joystick
	if (KeyID > 264)
	{
		if( g_pJoystick ) 
			if ( js[idx].rgbButtons[KeyID - 265] & 0x80 )
				return true;
	}
	//Check Joystick POVs
	if (KeyID > 296)
	{
		if( g_pJoystick[idx] ) 
		{
			switch (KeyID - 297)
			{
	
			case 0:
				{
					if (js[idx].rgdwPOV[0] == 0)
					{
						return true;
					}
					break;
				}
			case 1:
				{
					if ((js[idx].rgdwPOV[0] > 4400) & (js[idx].rgdwPOV[0] < 4600)) //branco
					{
						return true;
					}
					break;
				}
			case 2:
				{
					if ((js[idx].rgdwPOV[0] > 8900) & (js[idx].rgdwPOV[0] < 9100))//branco
					{
						return true;
					}
					break;
				}
			case 3:
				{
					
					if ((js[idx].rgdwPOV[0] > 13400) & (js[idx].rgdwPOV[0] < 13600))
					{
						return true;
					}
					break;
				}
			case 4:
				{
					if ((js[idx].rgdwPOV[0] > 17900) & (js[idx].rgdwPOV[0] < 18100))
					{
						return true;
					}
					break;
				}
			case 5:
				{
					if ((js[idx].rgdwPOV[0] > 22400) & (js[idx].rgdwPOV[0] < 22600))
					{
						return true;
					}
					break;
				}
			case 6:
				{
					if ((js[idx].rgdwPOV[0] > 26900) & (js[idx].rgdwPOV[0] < 27100))
					{
						return true;
					}
					break;
				}
			case 7:
				{
					if ((js[idx].rgdwPOV[0] > 31400) & (js[idx].rgdwPOV[0] < 31600))
					{
						return true;
					}
					break;
				}
			case 8:
				{
					if ((js[idx].rgdwPOV[0] > 35900) & (js[idx].rgdwPOV[0] < 36100))
					{
						return true;
					}
					break;
				}
			case 9:
				{
					if (js[idx].rgdwPOV[1] == 0)
					{
						return true;
					}
					break;
				}
			case 10:
				{
					if ((js[idx].rgdwPOV[1] > 4400) & (js[idx].rgdwPOV[1] < 4600))
					{
						return true;
					}
					break;
				}
			case 11:
				{
					if ((js[idx].rgdwPOV[1] > 8900) & (js[idx].rgdwPOV[1] < 9100))
					{
						return true;
					}
					break;
				}
			case 12:
				{
					if ((js[idx].rgdwPOV[1] > 13400) & (js[idx].rgdwPOV[1] < 13600))
					{
						return true;
					}
					break;
				}
			case 13:
				{
					if ((js[idx].rgdwPOV[1] > 17900) & (js[idx].rgdwPOV[1] < 18100))
					{
						return true;
					}
					break;
				}
			case 14:
				{
					if ((js[idx].rgdwPOV[1] > 22400) & (js[idx].rgdwPOV[1] < 22600))
					{
						return true;
					}
					break;
				}
			case 15:
				{
					if ((js[idx].rgdwPOV[1] > 26900) & (js[idx].rgdwPOV[1] < 27100))
					{
						return true;
					}
					break;
				}
			case 16:
				{
					if ((js[idx].rgdwPOV[1] > 31400) & (js[idx].rgdwPOV[1] < 31600))
					{
						return true;
					}
					break;
				}
			case 17:
				{
					if ((js[idx].rgdwPOV[1] > 35900) & (js[idx].rgdwPOV[1] < 36100))
					{
						return true;
					}
					break;
				}
			case 18:
				{
					if (js[idx].rgdwPOV[2] == 0)
					{
						return true;
					}
					break;
				}
			case 19:
				{
					if ((js[idx].rgdwPOV[2] > 4400) & (js[idx].rgdwPOV[2] < 4600))
					{
						return true;
					}
					break;
				}
			case 20:
				{
					if ((js[idx].rgdwPOV[2] > 8900) & (js[idx].rgdwPOV[2] < 9100))
					{
						return true;
					}
					break;
				}
			case 21:
				{
					if ((js[idx].rgdwPOV[2] > 13400) & (js[idx].rgdwPOV[2] < 13600))
					{
						return true;
					}
					break;
				}
			case 22:
				{
					if ((js[idx].rgdwPOV[2] > 17900) & (js[idx].rgdwPOV[2] < 18100))
					{
						return true;
					}
					break;
				}
			case 23:
				{
					if ((js[idx].rgdwPOV[2] > 22400) & (js[idx].rgdwPOV[2] < 22600))
					{
						return true;
					}
					break;
				}
			case 24:
				{
					if ((js[idx].rgdwPOV[2] > 26900) & (js[idx].rgdwPOV[2] < 27100))
					{
						return true;
					}
					break;
				}
			case 25:
				{
					if ((js[idx].rgdwPOV[2] > 31400) & (js[idx].rgdwPOV[2] < 31600))
					{
						return true;
					}
					break;
				}
			case 26:
				{
					if ((js[idx].rgdwPOV[2] > 35900) & (js[idx].rgdwPOV[2] < 36100))
					{
						return true;
					}
					break;
				}
			case 27:
				{
					if (js[idx].rgdwPOV[3] == 0)
					{
						return true;
					}
					break;
				}
			case 28:
				{
					if ((js[idx].rgdwPOV[3] > 4400) & (js[idx].rgdwPOV[3] < 4600))
					{
						return true;
					}
					break;
				}
			case 29:
				{
					if ((js[idx].rgdwPOV[3] > 8900) & (js[idx].rgdwPOV[3] < 9100))
					{
						return true;
					}
					break;
				}
			case 30:
				{
					if ((js[idx].rgdwPOV[3] > 13400) & (js[idx].rgdwPOV[3] < 13600))
					{
						return true;
					}
					break;
				}
			case 31:
				{
					if ((js[idx].rgdwPOV[3] > 17900) & (js[idx].rgdwPOV[3] < 18100))
					{
						return true;
					}
					break;
				}
			case 32:
				{
					if ((js[idx].rgdwPOV[3] > 22400) & (js[idx].rgdwPOV[3] < 22600))
					{
						return true;
					}
					break;
				}
			case 33:
				{
					if ((js[idx].rgdwPOV[3] > 26900) & (js[idx].rgdwPOV[3] < 27100))
					{
						return true;
					}
					break;
				}
			case 34:
				{
					if ((js[idx].rgdwPOV[3] > 31400) & (js[idx].rgdwPOV[3] < 31600))
					{
						return true;
					}
					break;
				}
			case 35:
				{
					if ((js[idx].rgdwPOV[3] > 35900) & (js[idx].rgdwPOV[3] < 36100))
					{
						return true;
					}
					break;
				}
			}
		}
	}
	return false;
	//max 296
}

HRESULT InitDirectInput( HWND hDlg, int joyindex )
{

    HRESULT hr; 
	hookWnd = hDlg;
    FreeDirectInput(0);
	didfree=false;

	wpOriginalWndProc = (WNDPROC)SetWindowLongW(hDlg, GWL_WNDPROC, GetWindowLong(hDlg,GWL_WNDPROC) ); 
	wpOriginalWndProc = (WNDPROC)SetWindowLongW(hDlg, GWL_WNDPROC, (LONG) NewWndProc); 
    // Create a DInput object
    if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION, 
                                         IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
        return hr;


	//Create Joystick
	for (int p = 0; p < 4; ++p)
	{
		//g_pJoystick[p]=NULL;
		if (jyindex[p] >= 0)
		{
			iindex=0;
			jpoint=p;
			jindex = jyindex[p];
			g_pJoystick[p]=NULL;

			g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY );
			if( g_pJoystick[p] )
			{
				
				
				g_pJoystick[p]->SetDataFormat( &c_dfDIJoystick2 );
				if (useforce[p] == 1)
				{
					g_pJoystick[p]->SetCooperativeLevel( hDlg, DISCL_EXCLUSIVE|DISCL_BACKGROUND );
				}
				else
				{
					g_pJoystick[p]->SetCooperativeLevel( hDlg, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND );
				}
				//g_pJoystick[p]->EnumObjects( EnumObjectsCallback, (VOID*)hDlg, DIDFT_ALL );
				g_dwNumForceFeedbackAxis[p]=0;
				g_pJoystick[p]->EnumObjects( EnumAxesCallback, (VOID*)&g_dwNumForceFeedbackAxis[p], DIDFT_AXIS );
				if(g_dwNumForceFeedbackAxis[p]==0)useforce[p]=0;
				//Set Resolution
				g_pJoystick[p]->EnumObjects( EnumObjectsCallback, (VOID*)hDlg, DIDFT_ALL );
				g_pJoystick[p]->Acquire();
				if (useforce[p] == 1 && g_dwNumForceFeedbackAxis[p]>0)
				{
					// Since we will be playing force feedback effects, we should disable the
					// auto-centering spring.
					DIPROPDWORD dipdw; 
 					dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
					dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
					dipdw.diph.dwObj        = 0;
					dipdw.diph.dwHow        = DIPH_DEVICE;
					dipdw.dwData            = FALSE;

					g_pJoystick[p]->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph );  

					// This simple sample only supports one or two axis joysticks
					if( g_dwNumForceFeedbackAxis[p] > 2 )
					g_dwNumForceFeedbackAxis[p] = 2;
					// This application needs only one effect: Applying raw forces.
					DWORD           rgdwAxes[2]     = { DIJOFS_X,DIJOFS_Y };
					LONG            rglDirection[2] = { 0, 0 };

					ZeroMemory( &eff, sizeof(eff) );
					ZeroMemory( &en, sizeof(en) );
					ZeroMemory( &c, sizeof(c) );
					ZeroMemory( &cf, sizeof(cf) );
					eff.dwSize                  = sizeof(DIEFFECT);
					eff.dwFlags                 = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
					en.dwSize = sizeof(DIENVELOPE);
					eff.dwSamplePeriod          = 0;
					eff.dwGain                  = DI_FFNOMINALMAX;
					eff.dwTriggerButton         = DIEB_NOTRIGGER;
					eff.dwTriggerRepeatInterval = 0;
					eff.cAxes                   = g_dwNumForceFeedbackAxis[p];
					eff.rgdwAxes                = rgdwAxes;
					eff.rglDirection            = rglDirection;
					//eff.lpEnvelope              = &en;
					eff.lpvTypeSpecificParams   = 0;
					eff.dwStartDelay            = 0;

					// Create the prepared effect
					for(int i=0;i<numforces;i++)
					{g_pEffect[p*numforces+i] = NULL;}

					int ifrc=0;
					if (iswheel[p])
					{
						
						en.dwFadeTime = 0;
						eff.dwDuration = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DIPERIODIC);
						eff.lpvTypeSpecificParams   = &cf;
						eff.dwGain                  = forcefgain[0][p];
						//eff.lpEnvelope              = &en;
						g_pJoystick[p]->CreateEffect( GUID_Sine, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						en.dwFadeTime = 0;
						eff.dwDuration = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DIPERIODIC);
						eff.lpvTypeSpecificParams   = &cf;
						eff.dwGain                  = forcefgain[1][p];
						//eff.lpEnvelope              = &en;
						g_pJoystick[p]->CreateEffect( GUID_Sine, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						c.lNegativeCoefficient = -10000;
						c.lPositiveCoefficient = -10000;
						c.lDeadBand = 0;
						eff.dwDuration = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DICONDITION);
						eff.lpvTypeSpecificParams   = &c;
						eff.dwGain                  = wheelinertia[p];
						//eff.lpEnvelope              = &en;
						g_pJoystick[p]->CreateEffect( GUID_Inertia, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						c.lNegativeCoefficient = 10000;
						c.lPositiveCoefficient = 10000;
						c.lDeadBand = 000;
						eff.dwDuration = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DICONDITION);
						eff.lpvTypeSpecificParams   = &c;
						eff.dwGain                  = wheelspring[p];
						//eff.lpEnvelope              = &en;
						g_pJoystick[p]->CreateEffect( GUID_Spring, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						c.lNegativeCoefficient = 10000;
						c.lPositiveCoefficient = 10000;
						c.lDeadBand = 0;
						eff.dwDuration = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DICONDITION);
						eff.lpvTypeSpecificParams   = &c;
						eff.dwGain                  = forcegain[p];
						//rgdwAxes[0] = DIJOFS_Y;
						//g_pJoystick[p]->CreateEffect( GUID_Friction, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						//eff.lpEnvelope              =0;
						//eff.lpvTypeSpecificParams   = &c;
						//eff.dwDuration              = INFINITE;
						//eff.cbTypeSpecificParams    = sizeof(DICONDITION);
						//g_pJoystick[p]->CreateEffect( GUID_Inertia, &eff, &g_pEffect[8], NULL );
					}
					else
					{
						cfw.lMagnitude = 0;
						eff.dwDuration              = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
						eff.lpvTypeSpecificParams   = &cfw;
						eff.dwGain                  = forcefgain[0][p];
						g_pJoystick[p]->CreateEffect( GUID_ConstantForce, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
						//g_pJoystick[p]->Acquire();
						
						// Create the prepared effect
						cfw.lMagnitude = 0;
						eff.dwDuration              = INFINITE;
						eff.cbTypeSpecificParams    = sizeof(DICONSTANTFORCE);
						eff.lpvTypeSpecificParams   = &cfw;
						eff.dwGain                  = forcefgain[1][p];
						g_pJoystick[p]->CreateEffect( GUID_ConstantForce, &eff, &g_pEffect[p*numforces+ifrc], NULL );
						if(g_pEffect[p*numforces+ifrc])ifrc++;
					}

					g_pJoystick[p]->Acquire();
					for (int f = 0; f < numforces; ++f)
					{
						if( g_pEffect[p*numforces+f] )
						{
							g_pEffect[p*numforces+f]->GetParameters(&eff,DIEP_DURATION);
							if(eff.dwDuration==INFINITE) g_pEffect[p*numforces+f]->SetParameters( &eff, DIEP_START);; // Start the effect
						}
					}
				}
			}
		}
	}
	//Create Keyboard
    g_pDI->CreateDevice( GUID_SysKeyboard, &g_pKeyboard, NULL );
	if( g_pKeyboard )
	{
		g_pKeyboard->SetDataFormat( &c_dfDIKeyboard );
		g_pKeyboard->SetCooperativeLevel( hDlg, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND );
		g_pKeyboard->Acquire();
	}

	//Create Mouse
    g_pDI->CreateDevice( GUID_SysMouse, &g_pMouse, NULL );
	if( g_pMouse )
	{
		g_pMouse->SetDataFormat( &c_dfDIMouse2 );
		g_pMouse->SetCooperativeLevel( hDlg, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND );
		g_pMouse->Acquire();
	}

	return S_OK;

}
void SpringOffset(long magnitude,DWORD idx)
{


    // Modifying an effect is basically the same as creating a new one, except
    // you need only specify the parameters you are modifying
    LONG rglDirection[2] = { 0, 0 };

        rglDirection[0] = 9000;
        rglDirection[1] = 0;



	
	if (iswheel[idx])
	{
		c.lNegativeCoefficient = 10000;
		c.lPositiveCoefficient = 10000;
		c.lOffset = (long)(magnitude * 0.30518509475997192297128208258309);
		eff.dwDuration = INFINITE;
		eff.cbTypeSpecificParams    = sizeof(DICONDITION);
		eff.lpvTypeSpecificParams   = &c;

		// Now set the new parameters and start the effect immediately.
		g_pEffect[idx*numforces+3]->SetParameters( &eff, DIEP_TYPESPECIFICPARAMS);
	}
}
//-----------------------------------------------------------------------------
// Name: SetDeviceForcesXY()
// Desc: Apply the X and Y forces to the effect we prepared.
//-----------------------------------------------------------------------------
void SmallForce(DWORD magnitude,DWORD idx, int direct)
{

if(!g_pEffect[idx*numforces+1]) return;
    // Modifying an effect is basically the same as creating a new one, except
    // you need only specify the parameters you are modifying
    LONG rglDirection[2] = { 0, 0 };

        rglDirection[0] = 9000;
        rglDirection[1] = 0;



	
	if (iswheel[idx])
	{
		cf.dwPeriod = forcefreq[1][idx];
		cf.dwPhase = 0;
		cf.dwMagnitude = magnitude;
		cf.lOffset=0;
		eff.rglDirection          = rglDirection;
		eff.cbTypeSpecificParams  = sizeof(DIPERIODIC);
		eff.lpvTypeSpecificParams = &cf;

		// Now set the new parameters and start the effect immediately.
		g_pEffect[idx*numforces+1]->SetParameters( &eff, DIEP_DIRECTION |
											DIEP_TYPESPECIFICPARAMS);
	}
	else
	{
		//LONG m = (magnitude * (5000.0 / 10000.0)) +5000;
		
		//if (m>5000)
		//	cfw.lMagnitude = m;
		//else
		//	cfw.lMagnitude = 0;
		cfw.lMagnitude=magnitude;
		eff.rglDirection          = rglDirection;
		eff.cbTypeSpecificParams  = sizeof(DICONSTANTFORCE);
		eff.lpvTypeSpecificParams = &cfw;

		// Now set the new parameters and start the effect immediately.
		g_pEffect[idx*numforces+1]->SetParameters( &eff, DIEP_DIRECTION |
											DIEP_TYPESPECIFICPARAMS);

	}
}
void LargeForce(DWORD magnitude, DWORD idx, int direct)
{
if(!g_pEffect[idx*numforces]) return;
    // Modifying an effect is basically the same as creating a new one, except
    // you need only specify the parameters you are modifying
    LONG rglDirection[2] = { 0, 0 };

        rglDirection[0] = 0;
        rglDirection[1] = 0;


	if (iswheel[idx])
	{
		cf.dwPeriod = forcefreq[0][idx];//- (magnitude*17);
		cf.dwPhase = 0;
		cf.dwMagnitude = magnitude;
		cf.lOffset=0;
		eff.rglDirection          = rglDirection;
		eff.cbTypeSpecificParams  = sizeof(DIPERIODIC);
		eff.lpvTypeSpecificParams = &cf;
		eff.cAxes                   = g_dwNumForceFeedbackAxis[idx];
		// Now set the new parameters and start the effect immediately.
		g_pEffect[idx*numforces]->SetParameters( &eff, DIEP_DIRECTION |
											DIEP_TYPESPECIFICPARAMS);
	}
	else
	{
		cfw.lMagnitude = magnitude;
		eff.rglDirection          = rglDirection;
		eff.cbTypeSpecificParams  = sizeof(DICONSTANTFORCE);
		eff.lpvTypeSpecificParams = &cfw;
		eff.cAxes                   = g_dwNumForceFeedbackAxis[idx];
		// Now set the new parameters and start the effect immediately.
		g_pEffect[idx*numforces]->SetParameters( &eff, DIEP_DIRECTION |
											DIEP_TYPESPECIFICPARAMS);
	}
	
}
void SwitchJoystick(HWND hDlg, int jid, DWORD idx)
{
	//if(jyindex[idx] == -1) return;
	if (!g_pDI) return;
	if (g_pJoystick[idx]) g_pJoystick[idx]->Unacquire();
	jindex = jid;
	
	g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY );
    if( g_pJoystick )
	{
		g_pJoystick[idx]->SetDataFormat( &c_dfDIJoystick2 );
		g_pJoystick[idx]->SetCooperativeLevel( hDlg, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND );
		g_pJoystick[idx]->EnumObjects( EnumObjectsCallback, (VOID*)hDlg, DIDFT_ALL );
		g_pJoystick[idx]->Acquire();
	}
}

// Helper function for polling joystick data

// idx must be between 0 and 127
bool ButtonPressed(int idx, DWORD jidx) {
	//if(jyindex[idx] == -1) return false;

	return (js[jidx].rgbButtons[idx] & 0x80) != 0;
}
// idx must be between 0 and 3
int POVState(int idx, DWORD jidx) {
	//if(jyindex[idx] == -1) return false;
	DWORD pov = js[jidx].rgdwPOV[idx];
	if (LOWORD(pov) == 0xFFFF) // Centered
		return -1;
	if (pov == 0) // Up
		return 0;
	if ((pov > 4400) && (pov < 4600)) // Up-right
		return 1;
	if ((pov > 8900) && (pov < 9100)) // Right
		return 2;
	if ((pov > 13400) && (pov < 13600)) // Down-right
		return 3;
	if ((pov > 17900) && (pov < 18100)) // Down
		return 4;
	if ((pov > 22400) && (pov < 22600)) // Down-left
		return 5;
	if ((pov > 26900) && (pov < 27100)) // Left
		return 6;
	if ((pov > 31400) && (pov < 31600)) // Up-left
		return 7;
	if ((pov > 35900) && (pov < 36100)) // Up
		return 8;
	// Default
	return -1;
}